// 作用域
//在Java中，我们经常看到public、protected、private这些修饰符。在Java中，这些修饰符可以用来限定访问作用域。


// public
//定义为public的class、interface可以被其他任何类访问：
package abc;

public class Hello {
	public void hi() {
		//...
	}
}

//上面的Hello是public,因此,可以被其他package的class访问到
package xyz;

class Main {
	void foo() {
		//Main可以访问Hello
		Hello h = new Hello();
	}
}


//定义为public的field、method可以被其他class访问,前提：访问的另外一个class的开放了权限
package abc;

public class Hello {
	public void hi() {
		//...
	}
}


//上面的hi()方法是public,可以被其他class访问/调用,前提是首先要能访问class Hello
package xyz;

class Main {
	void foo() {
		Hello h = new Hello();
		h.hi();
	}
}




// private
//定义为private的field、method无法被其他class访问:
package abc;

public class Hello {
	
	private void hi() {
		//这个hi()方法,不能被其他类调用
	}

	public void hello() {
		this.hi();
	}
}


//实际上，确切地说，private访问权限被限定在class的内部，而且与方法声明顺序无关。推荐把private方法放到后面，因为public方法定义了类对外提供的功能，阅读代码的时候，应该先关注public方法：
package abc;

public class Hello {
	public void hello() {
		this.hi();
	}

	private void hi() {
		System.out.printf("hi");
	}
}



//由于Java支持嵌套类，如果一个类内部还定义了嵌套类，那么，嵌套类拥有访问private的权限：
public class Main {
	public static void main(String[] args) {
		Inner i = new Inner();//嵌套class拥有访问private的权限
		i.hi();
	}

	// private method
	private static void hello() {
		System.out.println("private hello!");
	}

	//静态内部类
	static class Inner {
		public void hi() {
			Main.hello();//嵌套class拥有访问private的权限
		}
	}
}

//定义在一个class内部的class称为嵌套类（nested class），Java支持好几种嵌套类。




// protected
//protected作用于[继承关系].定义为protected的field和method可以被【子类】访问
package abc;

public class Hello {
	//protected method
	protected void hi() {}
}

//上面的protected method可以被继承的class访问
package xyz;
import abc;

class Main extends Hello {
	void foo {
		Hello h = new Hello();
		h.hi(); //ok
	}
}




// package
//最后，[包作用域]是指一个class允许访问同一个package的没有public、private修饰的class，以及没有public、protected、private修饰的字段和方法。
package abc;
//package权限的class
class Hello {
	//package method
	void hi() {} //没有public/private/protected的method就是package
}

//只要在同一个package,就可以访问package权限的class/field/method
package abc;

class Main {
	void foo() {
		//可以访问package权限的class
		Hello h = new Hello();

		//可以调用package
		h.hi();
	}
}

//注意,【包名】必须完全一致,包没有父子关系
//例如：com.apache和com.apache.abc是完全不同的2个包





// 局部变量
//在方法内部定义的变量称为【局部变量】，局部变量作用域从变量声明处开始到对应的块结束。【方法参数】也是局部变量。
package abc;

public class Hello {
	void hi(String name) { // 1
		String s = name.toLowerCase(); //2
		int len = s.length(); //3

		if (len < 10) { //4
			int p = 10 - len; //5

			for (int i=0; i<10; i++) { //6
				System.out.println(); //7
			} //8
		} //9
	} //10
}

/*
我们观察上面的hi()方法代码：

	1.方法参数name是局部变量，它的作用域是hi整个方法，即1~10；

	2.变量s的作用域是定义处到方法结束，即2~10；

	3.变量len的作用域是定义处到方法结束，即3~10；

	4.变量p的作用域是定义处到if块结束，即5~9；

	5.变量i的作用域是for循环，即6~8。

使用局部变量时，应该尽可能把局部变量的作用域缩小，尽可能延后声明局部变量。
*/




// final (上锁作用域)
/*
Java还提供了一个final修饰符。final与访问权限不冲突，它有很多作用。

用final修饰class可以阻止被继承：
*/
package abc;

//不允许继承
final public class Hello {
	private int n = 0;

	protected void hi(int t) {
		long i = t;
	}
}


//final method : 可以阻止继承后子类覆写
package abc;

public class Hello {
	//无法@Override这个hi()方法
	final protected void hi() {}
}


//final field : 可以阻止重新赋值
package abc;

public class Hello {
	final private int n = 0;//一旦赋值,就无法修改

	protected void hi() {
		this.n = 1;//修改失败
	}
}


// final 局部变量 : 可以阻止被重新赋值
package abc;

public class Hello {
	protected void hi(final int t) {
		t = 1; //出错,传入的t不允许再赋值
	}
}


/* 最佳实践

如果不确定是否需要public，就不声明为public，即尽可能少地暴露对外的字段和方法。

把方法定义为package权限有助于测试，因为【测试class】和【被测试class】只要位于同一个package，测试代码就可以访问被测试类的package权限方法。

#重点：
	一个【.java文件】只能包含【一个public类】，但可以包含【多个非public类】。如果有【public类】，【文件名】必须和【public类的名字】相同。

*/





/* 小结

1. Java内建的访问权限包括public、protected、private和package权限；

2. Java在方法【内部定义的变量】是【局部变量】，局部变量的【作用域】从变量声明开始，到一个【块】结束；

3. 【final修饰符】不是[访问权限]，它可以修饰class、field和method；

4. 一个【.java文件】只能包含[一个public类]，但可以包含[多个非public类]。

*/